package org.dna.wicketui;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.panel.Panel;
import org.dna.MetroApplication;
import org.dna.uimodel.MenuItem;
import org.dna.uimodel.SelectableActivity;
import org.dna.uimodel.IContextMenuProvider;
import org.tigris.scarab.model.Activity;

public abstract class MetroPage extends WebPage {

    private static final Logger LOG = Logger.getLogger(MetroPage.class);
    private SummaryPanel pnlSummary;
    protected Panel m_currentPanel;
    protected List<MenuItem> m_pageContextMenu;
    protected Map<Class<? extends Panel>, Panel> m_panelsMap = new HashMap<Class<? extends Panel>, Panel>();

    public MetroPage() {
        pnlSummary = new SummaryPanel("summaryPanel");
        add(pnlSummary);
        add(new MenuPanel("menuPanel"));
        add(new UserPanel("userPanel", LoginPage.class));
        
        m_currentPanel = configurePanels();
        //TODO verify that the default panel was inserted in the map.
        add(m_currentPanel);
        
        m_pageContextMenu = preparePageContextMenu();
        updateContextMenu();
        
    }
    
    /**
     * Configure the user panels for the page, returning the default starting
     * panel. The implementation create and adds the panels to the panels map.
     */
    protected abstract Panel configurePanels();
    
    /**
     * Return the definition of context menu for the page
     */
    protected abstract List<MenuItem> preparePageContextMenu();

    //TODO remove from here probably must stay in the MetroApplication (that is the global controller)
    public static List<SelectableActivity> allActivities() {
        List<Activity> activities = MetroApplication.get().getActivityDAO().allActivities();
        List<SelectableActivity> res = new ArrayList<SelectableActivity>(activities.size());
        for (Activity act : activities) {
            res.add(new SelectableActivity(act));
        }
        return res;
    }

    public void setContextMenu(List<MenuItem> contextmenu) {
        pnlSummary.setContextMenu(contextmenu);
    }

    /**
     * Updates the context menu shared by all page panels with the other menus
     * offered by the current panel.
     * */
    protected void updateContextMenu() {
        List<MenuItem> currentMenu = new ArrayList<MenuItem>(m_pageContextMenu);
        if (m_currentPanel instanceof IContextMenuProvider) {
            List<MenuItem> panelContextMenu = ((IContextMenuProvider) m_currentPanel).prepare();
            currentMenu.addAll(panelContextMenu);
        }
        setContextMenu(currentMenu);
    }

    /**
     * Switch the current displayed panel to that represented by the class passed
     * into.
     *
     * @param clazz the target panel's class to switch.
     * */
    public void switchToPanel(Class<? extends Panel> clazz) {
        Panel inst = m_panelsMap.get(clazz);
        if (inst == null) {
            throw new RuntimeException("Can't find a panel of class" + clazz
                    + " to switch");
        }
        m_currentPanel.replaceWith(inst);
        m_currentPanel = inst;
        updateContextMenu();
    }

    /**
     * Switch the current displayed panel to that represented by the class passing
     * the argument to the constructor.
     *
     * @param clazz the target panel's class to switch.
     * @param arg the constructor's argument for the target class.
     * */
    public void switchToPanel(Class<? extends Panel> clazz, Object arg) {
        Panel p = null;
        try {
            Constructor<? extends Panel> constr = clazz.getDeclaredConstructor(String.class, arg.getClass());
            p = constr.newInstance("innerPanel", arg);
        } catch (InstantiationException ex) {
            LOG.log(Level.FATAL, "Can't instantiate page " + clazz, ex);
        } catch (IllegalAccessException ex) {
            LOG.log(Level.FATAL, "Can't instantiate page " + clazz, ex);
        } catch (InvocationTargetException ex) {
            LOG.log(Level.FATAL, "Can't instantiate page " + clazz + ", constructor throw an exception", ex);
        } catch (NoSuchMethodException ex) {
            LOG.log(Level.FATAL, "Can't instantiate page " + clazz + " constructor not found", ex);
        } catch (SecurityException ex) {
            LOG.log(Level.FATAL, null, ex);
        }

        if (p == null) {
            return;
        }
        m_currentPanel.replaceWith(p);
        m_currentPanel = p;
        updateContextMenu();
    }
}
